/*
 * Copyright 2020-2025 the original author or authors.
 * You cannot use this file unless authorized by the author.
 */

package org.ipig.commons.collections;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * <ul>
 * 基于LRU（The Least Recently Used，最近最久未使用算法）算法的LinkedHashMap的可重入互斥锁同步版本，当Map容量超过限制时自动删除最少使用的Entry，并调用processRemovedItem方法
 * <li>LRU算法的思想是：如果一个数据在最近一段时间没有被访问到，那么可以认为在将来它被访问的可能性也很小。因此，当空间满时，最久没有访问的数据最先被置换（淘汰）</>
 * </ul>
 *
 * @author <a href="mailto:comchnts@163.com">chinats</a>
 * @version $$Id: LeastRecentlyUsedCache.java 3 2020-01-21 09:36:33Z ts <comchnts@163.com> $$
 * @since 1.0
 */
public class LeastRecentlyUsedCache<K, V> extends LinkedHashMap<K, V> {
    private final int maxCapacity;
    private static final float DEFAULT_LOAD_FACTOR = 0.75f;
    private final Lock lock = new ReentrantLock();

    public LeastRecentlyUsedCache(int maxCapacity) {
        super(maxCapacity, LeastRecentlyUsedCache.DEFAULT_LOAD_FACTOR, true);
        this.maxCapacity = maxCapacity;
    }

    protected void processRemovedItem(java.util.Map.Entry<K, V> eldest) {
    }

    /**
     * 方法说明：判断是否需要删除最少使用的Entry
     *
     * @param eldest
     * @return
     * @see java.util.LinkedHashMap#removeEldestEntry(java.util.Map.Entry)
     */
    @Override
    protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
        boolean isRemove = this.size() > this.maxCapacity;
        if (isRemove) {
            this.processRemovedItem(eldest);
        }
        return isRemove;
    }

    @Override
    public boolean containsKey(Object key) {
        try {
            this.lock.lock();
            return super.containsKey(key);
        } finally {
            this.lock.unlock();
        }
    }

    @Override
    public V get(Object key) {
        try {
            this.lock.lock();
            return super.get(key);
        } finally {
            this.lock.unlock();
        }
    }

    @Override
    public V put(K key, V value) {
        try {
            this.lock.lock();
            return super.put(key, value);
        } finally {
            this.lock.unlock();
        }
    }

    @Override
    public int size() {
        try {
            this.lock.lock();
            return super.size();
        } finally {
            this.lock.unlock();
        }
    }

    @Override
    public void clear() {
        try {
            this.lock.lock();
            super.clear();
        } finally {
            this.lock.unlock();
        }
    }

    public Collection<Map.Entry<K, V>> getAll() {
        try {
            this.lock.lock();
            return new ArrayList<Map.Entry<K, V>>(super.entrySet());
        } finally {
            this.lock.unlock();
        }
    }
}
